/*******************************************************************************
 * Copyright (c) 2007 Jeff Mesnil
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *      Benjamin Walstrum (issue #24)
 *******************************************************************************/

package net.jmesnil.jmx.ui.internal.controls;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import net.jmesnil.jmx.ui.JMXUIActivator;
import net.jmesnil.jmx.ui.extensions.IAttributeControlFactory;
import net.jmesnil.jmx.ui.extensions.IWritableAttributeHandler;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.forms.widgets.FormToolkit;

public class AttributeControlFactory {

    private static final Map<String, List<IAttributeControlFactory>> typeFactories;
    private static final Map<String, Map<Pattern, IAttributeControlFactory>> patternFactories;
    
    private static final IAttributeControlFactory defaultFactory = new TextControlFactory();
    private static final IAttributeControlFactory arrayFactory = new ArrayControlFactory();
    
    static {
        Map<String, List<IAttributeControlFactory>> typeMap =
            new HashMap<String, List<IAttributeControlFactory>>();
        Map<String, IAttributeControlFactory> idMap = 
            new HashMap<String, IAttributeControlFactory>();

        IExtensionRegistry registry = Platform.getExtensionRegistry();
        IExtensionPoint epDisplays = 
                registry.getExtensionPoint("net.jmesnil.jmx.ui.attribute.controls"); //$NON-NLS-1$
        for (IConfigurationElement element : epDisplays.getConfigurationElements()) {
            String id = element.getAttribute("id"); //$NON-NLS-1$
            String type = element.getAttribute("type"); //$NON-NLS-1$
            IAttributeControlFactory factory = null;
            try {
                factory = (IAttributeControlFactory) element.createExecutableExtension("class"); //$NON-NLS-1$
            } catch (CoreException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                continue;
            }
            if (factory != null) {
                List<IAttributeControlFactory> displays = typeMap.get(type);
                if (displays == null) {
                    displays = new ArrayList<IAttributeControlFactory>(1);
                    typeMap.put(type, displays);
                }
                displays.add(factory);
            }
            idMap.put(id, factory);
        }
        
        Map<String, Map<Pattern, IAttributeControlFactory>> patternMap =
            new HashMap<String, Map<Pattern, IAttributeControlFactory>>();
        
        IExtensionPoint bindings = 
                registry.getExtensionPoint("net.jmesnil.jmx.ui.attribute.bindings"); //$NON-NLS-1$
        for (IConfigurationElement element : bindings.getConfigurationElements()) {
            IAttributeControlFactory factory = idMap.get(element.getAttribute("controlID")); //$NON-NLS-1$
            String name = element.getAttribute("name"); //$NON-NLS-1$
            Pattern pattern = Pattern.compile(element.getAttribute("objectName")); //$NON-NLS-1$

            Map<Pattern, IAttributeControlFactory> factoryMap = patternMap.get(name);
            if (factoryMap == null) {
                factoryMap = new HashMap<Pattern, IAttributeControlFactory>();
                patternMap.put(name, factoryMap);
            }
            factoryMap.put(pattern, factory);
        }
        
        typeFactories = Collections.unmodifiableMap(typeMap);
        patternFactories = Collections.unmodifiableMap(patternMap);
    }
    
    public static Control createControl(final Composite parent, final Object value) {
        return createControl(parent, value, value.getClass().getSimpleName(), 
                null, null, false, null, null);
    }

    public static Control createControl(final Composite parent, final Object value, 
            final String type, final String objectName, final String attributeName,
            final boolean writable, final IWritableAttributeHandler handler, 
            final FormToolkit toolkit) {
        IAttributeControlFactory factory = null;

        if (objectName != null && attributeName != null) {
            Map<Pattern, IAttributeControlFactory> patterns = patternFactories.get(attributeName);
            if (patterns != null) {
                for (Map.Entry<Pattern, IAttributeControlFactory> entry : patterns.entrySet()) {
                    if (entry.getKey().matcher(objectName).matches()) {
                        factory = entry.getValue();
                    }
                }
            }
        }
        
        if (factory == null) {
        	Class<? extends Object> clazz = value == null ? getClass(type) : value.getClass();
        	if (clazz == null) {
        		factory = defaultFactory;
        	} else {
                List<IAttributeControlFactory> factories = findFactories(clazz);
                if (factories == null) {
                    factory = value.getClass().isArray() ? arrayFactory : defaultFactory;
                } else {
                    factory = factories.get(0);
                }
        	}
        }
        
        return factory.createControl(parent, toolkit, writable, type, value, handler);
    }

    private static List<IAttributeControlFactory> findFactories(final Class<? extends Object> valueClass) {
        for (Map.Entry<String, List<IAttributeControlFactory>> entry : typeFactories.entrySet()) {
            try {
                if (Class.forName(entry.getKey()).isAssignableFrom(valueClass)) {
                    return entry.getValue();
                }
            } catch (ClassNotFoundException e) {
                JMXUIActivator.log(IStatus.WARNING, e.getMessage(), e);
            }
        }
        return null;
    }
    
    private static Class<? extends Object> getClass(final String type) {
    	Class<? extends Object> result = null;
    	if (type.equals("int")) { //$NON-NLS-1$
    		result = Integer.class;
    	} else if (type.equals("boolean")) { //$NON-NLS-1$
    		result = Boolean.class;
    	} else if (type.equals("short")) { //$NON-NLS-1$
    		result = Short.class;
    	} else if (type.equals("long")) { //$NON-NLS-1$
    		result = Long.class;
    	} else if (type.equals("char")) { //$NON-NLS-1$
    		result = Character.class;
    	} else if (type.equals("byte")) { //$NON-NLS-1$
    		result = Byte.class;
    	} else if (type.equals("float")) { //$NON-NLS-1$
    		result = Float.class;
    	} else {
    		try {
				result = Class.forName(type);
			} catch (ClassNotFoundException e) {}
    	}
    	return result;
    }

}
